/*
 * @(#)IntegerEditor.java	11/06/02
 * Copyleft 2011. All rights reversed.
 *
 * This file is part of AverageGrade.
 *
 * AverageGrade is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AverageGrade is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with AverageGrade. If not, see <http://www.gnu.org/licenses/>.
 */

package net.grade.averagegrade.swing.editor;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.swing.AbstractAction;
import javax.swing.DefaultCellEditor;
import javax.swing.JFormattedTextField;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.NumberFormatter;

/**
 * IntegerEditor is a cell editor for a JTable, which supports integer
 * inputs between two limiting numbers (i.e. a minimum and maximum value).
 * In the event that a user inputs an invalid value in the cell, the cells
 * focus will remain until the value is changed to that of a valid one.
 */
public class IntegerEditor extends DefaultCellEditor {

    /** The TextField used for formatting the input values. */
    private final JFormattedTextField formattedTextField;

    /** The NumberFormat used for formatting/limiting/parsing input values. */
    private final NumberFormat integerFormat;

    /**
     * Creates a new instance of <code>IntegerEditor</code> using the specified
     * numbers as the bounds for the allowed input values. Both numbers are
     * inclusive, so that the allowed inputs will be integers n where:
     * <blockquote>
     * minimum <= n <= maximum
     * </blockquote>
     *
     * @param minimum The minimum value to allow inputs for (inclusive).
     * @param maximum The maximum value to allow inputs for (inclusive).
     */
    public IntegerEditor(final int minimum, final int maximum) {
        super(new JFormattedTextField());
        formattedTextField = (JFormattedTextField)getComponent();

        integerFormat = NumberFormat.getIntegerInstance();
        final NumberFormatter integerFormatter = new NumberFormatter(integerFormat);
        integerFormatter.setMinimum(minimum);
        integerFormatter.setMaximum(maximum);

        formattedTextField.setFormatterFactory(new DefaultFormatterFactory(integerFormatter));
        formattedTextField.setHorizontalAlignment(JTextField.LEFT);
        formattedTextField.setFocusLostBehavior(JFormattedTextField.PERSIST);
        formattedTextField.setValue(minimum);

        formattedTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ret");
        formattedTextField.getActionMap().put("ret", new AbstractAction() {
            public void actionPerformed(final ActionEvent e) {
                if(formattedTextField.isEditValid()) {
                    try {
                        formattedTextField.commitEdit();
                        formattedTextField.postActionEvent();
                    } catch(final ParseException ignored) { }
                }
            }
        });
    }

    @Override
    public Component getTableCellEditorComponent(final JTable table, final Object value,
            final boolean isSelected, final int row, final int column) {

        formattedTextField.setValue(value);
        return formattedTextField;
    }

    @Override
    public Object getCellEditorValue() {
        final Object cellValue = formattedTextField.getValue();
        if(cellValue instanceof Integer)
            return cellValue;
        if(cellValue instanceof Number)
            return ((Number)cellValue).intValue();

        try {
            return integerFormat.parseObject(cellValue.toString());
        } catch(final ParseException e) { return null; }
    }

    @Override
    public boolean stopCellEditing() {
        if(formattedTextField.isEditValid()) {
            try {
                formattedTextField.commitEdit();
            } catch(final ParseException ignored) { }

            return super.stopCellEditing();
        }

        return false;
    }
}